home *** CD-ROM | disk | FTP | other *** search
/ Almathera Ten Pack 2: CDPD 1 / Almathera Ten on Ten - Disc 2: CDPD 1.iso / pd / 201-225 / 217 / stevie / cmdline.c < prev    next >
C/C++ Source or Header  |  1995-03-13  |  15KB  |  727 lines

  1. /*
  2.  * STEVIE - Simply Try this Editor for VI Enthusiasts
  3.  *
  4.  * Code Contributions By : Tim Thompson           twitch!tjt
  5.  *                         Tony Andrews           onecom!wldrdg!tony 
  6.  *                         G. R. (Fred) Walter    watmath!watcgl!grwalter 
  7.  */
  8.  
  9. #include "stevie.h"
  10.  
  11. static char    *altfile = NULL;    /* alternate file */
  12. static int      altline;    /* line # in alternate file */
  13.  
  14. static char    *nowrtmsg = "No write since last change (use ! to override)";
  15.  
  16. extern char   **files;        /* used for "n" and "rew" */
  17. extern int      curfile;
  18. extern int      numfiles;
  19.  
  20. /*
  21.  * The next two variables contain the bounds of any range given in a command.
  22.  * If no range was given, both contain null line pointers. If only a single
  23.  * line was given, u_pos will contain a null line pointer. 
  24.  */
  25. static LPtr     l_pos, u_pos;
  26.  
  27. static bool_t   interactive;    /* TRUE if we're reading a real command line */
  28.  
  29. static bool_t   doecmd();
  30. static void
  31. badcmd(), doshell(), get_range();
  32. static LPtr    *get_line();
  33.  
  34. #ifdef  MEGAMAX
  35. overlay "cmdline"
  36. #endif
  37.  
  38. /*
  39.  * readcmdline() - accept a command line starting with ':', '/', or '?' 
  40.  *
  41.  * readcmdline() accepts and processes colon commands and searches. If
  42.  * 'cmdline' is null, the command line is read here. Otherwise, cmdline
  43.  * points to a complete command line that should be used. This is used in
  44.  * main() to handle initialization commands in the environment variable
  45.  * "EXINIT". 
  46.  */
  47. void
  48. readcmdline(firstc, cmdline)
  49.     char            firstc;    /* either ':', '/', or '?' */
  50.     char           *cmdline;    /* optional command string */
  51. {
  52.     char            c;
  53.     char            buff[CMDBUFFSIZE];
  54.     char            cmdbuf[CMDBUFFSIZE];
  55.     char            argbuf[CMDBUFFSIZE];
  56.     char           *p, *q, *cmd, *arg;
  57.     bool_t          literal_next_flag = FALSE;
  58.  
  59.     /*
  60.      * Clear the range variables. 
  61.      */
  62.     l_pos.linep = (LINE *) NULL;
  63.     u_pos.linep = (LINE *) NULL;
  64.  
  65.     interactive = (cmdline == NULL);
  66.  
  67.     if (interactive)
  68.     gotocmdline(YES, firstc);
  69.     p = buff;
  70.     if (firstc != ':')
  71.     *p++ = firstc;
  72.  
  73.     if (interactive) {
  74.     /* collect the command string, handling '\b' and @ */
  75.     for (;;) {
  76.         c = vgetc();
  77.         if (c == CTRL('V') && !literal_next_flag) {
  78.         literal_next_flag = TRUE;
  79.         outchar('^');
  80.         continue;
  81.         }
  82.         if (c == '\n' || ((c == '\r' || c == ESC) && (!literal_next_flag)))
  83.         break;
  84.         if ((c == '\b') && (!literal_next_flag)) {
  85.         if (p > buff + (firstc != ':')) {
  86.             p--;
  87.             /*
  88.              * this is gross, but it relies only on 'gotocmdline' 
  89.              */
  90.             gotocmdline(YES, firstc == ':' ? ':' : NUL);
  91.             for (q = buff; q < p; q++)
  92.             outstr(chars[*q].ch_str);
  93.         } else {
  94.             msg("");
  95.             return;    /* back to cmd mode */
  96.         }
  97.         continue;
  98.         }
  99.         if ((c == '@') && (!literal_next_flag)) {
  100.         p = buff;
  101.         gotocmdline(YES, firstc);
  102.         continue;
  103.         }
  104.         if (literal_next_flag) {
  105.         literal_next_flag = FALSE;
  106.         outchar('\b');
  107.         }
  108.         outstr(chars[c].ch_str);
  109.         *p++ = c;
  110.     }
  111.     *p = '\0';
  112.     } else {
  113.     if (strlen(cmdline) > CMDBUFFSIZE - 2)    /* should really do something
  114.                          * better here... */
  115.         return;
  116.     strcpy(p, cmdline);
  117.     }
  118.  
  119.     /* skip any initial white space */
  120.     for (cmd = buff; *cmd != NUL && isspace(*cmd); cmd++);
  121.  
  122.     /* search commands */
  123.     c = *cmd;
  124.     if (c == '/' || c == '?') {
  125.     cmd++;
  126.     /* was the command was '//' or '??' (I.E. repeat last search) */
  127.     if ((*cmd == c) || (*cmd == NUL)) {
  128.         if (c == '/')
  129.         searchagain(FORWARD);
  130.         else
  131.         searchagain(BACKWARD);
  132.         return;
  133.     }
  134.     /* If there is a matching '/' or '?' at the end, toss it */
  135.     p = strchr(cmd, NUL);
  136.     if (*(p - 1) == c && *(p - 2) != '\\')
  137.         *(p - 1) = NUL;
  138.     dosearch((c == '/') ? FORWARD : BACKWARD, cmd);
  139.     return;
  140.     }
  141.     /*
  142.      * Parse a range, if present (and update the cmd pointer). 
  143.      */
  144.     get_range(&cmd);
  145.     if (l_pos.linep != NULL) {
  146.     if (LINEOF(&l_pos) > LINEOF(&u_pos)) {
  147.         emsg("Invalid range");
  148.         return;
  149.     }
  150.     }
  151.     strcpy(cmdbuf, cmd);    /* save the unmodified command */
  152.  
  153.     /* isolate the command and find any argument */
  154.     for (p = cmd; *p != NUL && !isspace(*p); p++);
  155.     if (*p == NUL)
  156.     arg = NULL;
  157.     else {
  158.     *p = NUL;
  159.     for (p++; *p != NUL && isspace(*p); p++);
  160.     if (*p == NUL) {
  161.         arg = NULL;
  162.     } else {
  163.         strcpy(argbuf, p);
  164.         arg = argbuf;
  165.     }
  166.     }
  167.  
  168.     if (strcmp(cmd, "q!") == 0) {
  169.     getout(0);
  170.     }
  171.     if (strcmp(cmd, "q") == 0) {
  172.     if (Changed) {
  173.         emsg(nowrtmsg);
  174.     } else {
  175.         getout(0);
  176.     }
  177.     return;
  178.     }
  179.     if (strcmp(cmd, "w") == 0) {
  180.     if (arg == NULL) {
  181.         if (Filename != NULL) {
  182.         if (!writeit(Filename, &l_pos, &u_pos)) {
  183.             emsg("Problems occured while writing output file");
  184.         }
  185.         } else {
  186.         emsg("No output file");
  187.         }
  188.     } else {
  189.         (void) writeit(arg, &l_pos, &u_pos);
  190.     }
  191.     return;
  192.     }
  193.     if (strcmp(cmd, "wq") == 0) {
  194.     if (Filename != NULL) {
  195.         if (writeit(Filename, (LPtr *) NULL, (LPtr *) NULL)) {
  196.         getout(0);
  197.         }
  198.     } else {
  199.         emsg("No output file");
  200.     }
  201.     return;
  202.     }
  203.     if (strcmp(cmd, "x") == 0) {
  204.     if (Changed) {
  205.         if (Filename != NULL) {
  206.         if (!writeit(Filename, (LPtr *) NULL, (LPtr *) NULL)) {
  207.             emsg("Problems occured while writing output file");
  208.             return;
  209.         }
  210.         } else {
  211.         emsg("No output file");
  212.         return;
  213.         }
  214.     }
  215.     getout(0);
  216.     }
  217.     if (strcmp(cmd, "f") == 0 && arg == NULL) {
  218.     fileinfo();
  219.     return;
  220.     }
  221.     if (*cmd == 'n') {
  222.     if ((curfile + 1) < numfiles) {
  223.         /*
  224.          * stuff ":e[!] FILE\n" 
  225.          */
  226.         stuffReadbuff(":e");
  227.         if (cmd[1] == '!')
  228.         stuffReadbuff("!");
  229.         stuffReadbuff(" ");
  230.         stuffReadbuff(files[++curfile]);
  231.         stuffReadbuff("\n");
  232.     } else
  233.         emsg("No more files!");
  234.     return;
  235.     }
  236.     if (*cmd == 'p') {
  237.     if (curfile > 0) {
  238.         /*
  239.          * stuff ":e[!] FILE\n" 
  240.          */
  241.         stuffReadbuff(":e");
  242.         if (cmd[1] == '!')
  243.         stuffReadbuff("!");
  244.         stuffReadbuff(" ");
  245.         stuffReadbuff(files[--curfile]);
  246.         stuffReadbuff("\n");
  247.     } else
  248.         emsg("No more files!");
  249.     return;
  250.     }
  251.     if (strncmp(cmd, "rew", 3) == 0) {
  252.     if (numfiles <= 1)    /* nothing to rewind */
  253.         return;
  254.     curfile = 0;
  255.     /*
  256.      * stuff ":e[!] FILE\n" 
  257.      */
  258.     stuffReadbuff(":e");
  259.     if (cmd[3] == '!')
  260.         stuffReadbuff("!");
  261.     stuffReadbuff(" ");
  262.     stuffReadbuff(files[0]);
  263.     stuffReadbuff("\n");
  264.     return;
  265.     }
  266.     if (strcmp(cmd, "e") == 0) {
  267.     if (Changed)
  268.         emsg(nowrtmsg);
  269.     else
  270.         (void) doecmd(arg);
  271.     return;
  272.     }
  273.     if (strcmp(cmd, "e!") == 0) {
  274.     (void) doecmd(arg);
  275.     return;
  276.     }
  277.     if (strcmp(cmd, "f") == 0) {
  278.     Filename = strsave(arg);
  279.     filemess("");
  280.     return;
  281.     }
  282.     if (strcmp(cmd, "r") == 0 || strcmp(cmd, ".r") == 0) {
  283.     if (arg == NULL) {
  284.         badcmd();
  285.         return;
  286.     }
  287.     if (readfile(arg, Curschar, 1)) {
  288.         emsg("Can't open file");
  289.         return;
  290.     }
  291.     CHANGED;
  292.     return;
  293.     }
  294.     if (strcmp(cmd, ".=") == 0) {
  295.     smsg("line %d", cntllines(Filemem, Curschar));
  296.     return;
  297.     }
  298.     if (strcmp(cmd, "$=") == 0) {
  299.     smsg("%d", cntllines(Filemem, Fileend) - 1);
  300.     return;
  301.     }
  302.     if (strncmp(cmd, "ta", 2) == 0) {
  303.     dotag(arg, cmd[2] == '!');
  304.     return;
  305.     }
  306.     if (strcmp(cmd, "set") == 0) {
  307.     doset(arg, interactive);
  308.     return;
  309.     }
  310.     if (strcmp(cmd, "help") == 0) {
  311.     if (help())
  312.         s_clear();
  313.     return;
  314.     }
  315.     if (strcmp(cmd, "version") == 0) {
  316.     extern char    *Version;
  317.  
  318.     msg(Version);
  319.     return;
  320.     }
  321.     if (strcmp(cmd, "sh") == 0) {
  322.     doshell();
  323.     return;
  324.     }
  325.     if (strncmp(cmd, "d", 1) == 0) {
  326.     LINE           *cp;
  327.     int             n;
  328.  
  329.     if (l_pos.linep == NULL)
  330.         l_pos = *Curschar;
  331.     if (u_pos.linep == NULL)
  332.         u_pos = l_pos;
  333.  
  334.     ResetBuffers();
  335.     n = RowNumber(&l_pos);
  336.     AppendPositionToUndoUndobuff(0, n);
  337.     AppendPositionToUndobuff(0, n);
  338.     if ((Filetop->linep->next == l_pos.linep) &&
  339.         (u_pos.linep->next == Fileend->linep))
  340.         AppendToUndobuff("a");
  341.     else if (u_pos.linep->next == Fileend->linep)
  342.         AppendToUndobuff("o");
  343.     else
  344.         AppendToUndobuff("O");
  345.  
  346.     n = 0;
  347.     cp = l_pos.linep;
  348.     for (; cp != NULL && cp != Fileend->linep; cp = cp->next) {
  349.         AppendToUndobuff(cp->s);
  350.         n++;
  351.         if (cp == u_pos.linep)
  352.         break;
  353.         AppendToUndobuff(NL_STR);
  354.     }
  355.     AppendToUndobuff(ESC_STR);
  356.  
  357.     if (n > 1)
  358.         AppendNumberToUndoUndobuff(n);
  359.     AppendToUndoUndobuff("dd");
  360.  
  361.     *Curschar = l_pos;
  362.     delline(n);
  363.     S_NOT_VALID;
  364.     return;
  365.     }
  366.     if (strncmp(cmd, "s/", 2) == 0) {
  367.     dosub(&l_pos, &u_pos, cmdbuf + 1);
  368.     return;
  369.     }
  370.     if (strncmp(cmd, "g/", 2) == 0) {
  371.     doglob(&l_pos, &u_pos, cmdbuf + 1);
  372.     return;
  373.     }
  374.     /*
  375.      * If we got a line, but no command, then go to the line. 
  376.      */
  377.     if (*cmd == NUL && l_pos.linep != NULL) {
  378.     if (u_pos.linep != NULL)
  379.         *Curschar = u_pos;
  380.     else
  381.         *Curschar = l_pos;
  382.  
  383.     S_CHECK_TOPCHAR_AND_BOTCHAR;
  384.  
  385.     return;
  386.     }
  387.     badcmd();
  388. }
  389.  
  390. /*
  391.  * get_range - parse a range specifier 
  392.  *
  393.  * Ranges are of the form: 
  394.  *
  395.  * addr[,addr] 
  396.  *
  397.  * where 'addr' is: 
  398.  *
  399.  * %          (entire file)
  400.  * $  [+-NUM]
  401.  * 'x [+-NUM] (where x denotes a currently defined mark)
  402.  * .  [+-NUM]
  403.  * NUM 
  404.  *
  405.  * The pointer *cp is updated to point to the first character following the
  406.  * range spec. If an initial address is found, but no second, the upper bound
  407.  * is equal to the lower. 
  408.  */
  409. static void
  410. get_range(cp)
  411.     char          **cp;
  412. {
  413.     LPtr           *l;
  414.     char           *p;
  415.  
  416.     if (**cp == '%') {
  417.     l_pos.index = 0;
  418.     l_pos.linep = Filetop->linep->next;
  419.     u_pos.index = 0;
  420.     u_pos.linep = Fileend->linep->prev;
  421.     (*cp)++;
  422.     return;
  423.     }
  424.     if ((l = get_line(cp)) == NULL)
  425.     return;
  426.  
  427.     l_pos = *l;
  428.  
  429.     for (p = *cp; *p != NUL && isspace(*p); p++);
  430.  
  431.     *cp = p;
  432.  
  433.     if (*p != ',') {        /* is there another line spec ? */
  434.     u_pos = l_pos;
  435.     return;
  436.     }
  437.     *cp = ++p;
  438.  
  439.     if ((l = get_line(cp)) == NULL) {
  440.     u_pos = l_pos;
  441.     return;
  442.     }
  443.     u_pos = *l;
  444. }
  445.  
  446. static LPtr    *
  447. get_line(cp)
  448.     char          **cp;
  449. {
  450.     static LPtr     pos;
  451.     LPtr           *lp;
  452.     char           *p, c;
  453.     int             lnum;
  454.  
  455.     pos.index = 0;        /* shouldn't matter... check back later */
  456.  
  457.     p = *cp;
  458.     /*
  459.      * Determine the basic form, if present. 
  460.      */
  461.     switch (c = *p++) {
  462.  
  463.       case '$':
  464.     pos.linep = Fileend->linep->prev;
  465.     break;
  466.  
  467.       case '.':
  468.     pos.linep = Curschar->linep;
  469.     break;
  470.  
  471.       case '\'':
  472.     if ((lp = getmark(*p++)) == NULL) {
  473.         emsg("Unknown mark");
  474.         return (LPtr *) NULL;
  475.     }
  476.     pos = *lp;
  477.     break;
  478.  
  479.       case '0':
  480.       case '1':
  481.       case '2':
  482.       case '3':
  483.       case '4':
  484.       case '5':
  485.       case '6':
  486.       case '7':
  487.       case '8':
  488.       case '9':
  489.     for (lnum = c - '0'; isdigit(*p); p++)
  490.         lnum = (lnum * 10) + (*p - '0');
  491.  
  492.     if (lnum == 0)
  493.         lnum = 1;
  494.  
  495.     pos = *gotoline(lnum);
  496.     break;
  497.  
  498.       default:
  499.     return (LPtr *) NULL;
  500.     }
  501.  
  502.     while (*p != NUL && isspace(*p))
  503.     p++;
  504.  
  505.     if (*p == '-' || *p == '+') {
  506.     bool_t          neg = (*p++ == '-');
  507.  
  508.     for (lnum = 0; isdigit(*p); p++)
  509.         lnum = (lnum * 10) + (*p - '0');
  510.  
  511.     if (neg)
  512.         lnum = -lnum;
  513.  
  514.     pos = *gotoline(cntllines(Filemem, &pos) + lnum);
  515.     }
  516.     *cp = p;
  517.     return &pos;
  518. }
  519.  
  520. static void
  521. badcmd()
  522. {
  523.     if (interactive)
  524.     emsg("Unrecognized command");
  525. }
  526.  
  527. /*
  528.  * dotag(tag, force) - goto tag 
  529.  */
  530. void
  531. dotag(tag, force)
  532.     char           *tag;
  533.     bool_t          force;
  534. {
  535.     FILE           *tp, *fopen();
  536.     char            lbuf[LSIZE];
  537.     char           *fname, *str;
  538.  
  539.     if ((tp = fopen("tags", "r")) == NULL) {
  540.     emsg("Can't open tags file");
  541.     return;
  542.     }
  543.     while (fgets(lbuf, LSIZE, tp) != NULL) {
  544.     if ((fname = strchr(lbuf, TAB)) == NULL) {
  545.         emsg("Format error in tags file");
  546.         return;
  547.     }
  548.     *fname++ = '\0';
  549.     if ((str = strchr(fname, TAB)) == NULL) {
  550.         emsg("Format error in tags file");
  551.         return;
  552.     }
  553.     *str++ = '\0';
  554.  
  555.     if (strcmp(lbuf, tag) == 0) {
  556.         if (!force && Changed) {
  557.         emsg(nowrtmsg);
  558.         return;
  559.         }
  560.         if (doecmd(fname)) {
  561.         stuffReadbuff(str);    /* str has \n at end */
  562.         stuffReadbuff("\007");    /* CTRL('G') */
  563.         fclose(tp);
  564.         return;
  565.         }
  566.     }
  567.     }
  568.     emsg("tag not found");
  569.     fclose(tp);
  570. }
  571.  
  572. static          bool_t
  573. doecmd(arg)
  574.     char           *arg;
  575. {
  576.     int             line = 1;    /* line # to go to in new file */
  577.  
  578.     if (arg != NULL) {
  579.     /*
  580.      * First detect a ":e" on the current file. This is mainly for ":ta"
  581.      * commands where the destination is within the current file. 
  582.      */
  583.     if (Filename != NULL) {
  584.         if (strcmp(arg, Filename) == 0) {
  585.         if (!Changed) {
  586.             return TRUE;
  587.         }
  588.         }
  589.     }
  590.     if (strcmp(arg, "#") == 0) {    /* alternate */
  591.         char           *s = Filename;
  592.  
  593.         if (altfile == NULL) {
  594.         emsg("No alternate file");
  595.         return FALSE;
  596.         }
  597.         Filename = altfile;
  598.         altfile = s;
  599.         line = altline;
  600.         altline = cntllines(Filemem, Curschar);
  601.     } else {
  602.         altfile = Filename;
  603.         altline = cntllines(Filemem, Curschar);
  604.         Filename = strsave(arg);
  605.     }
  606.     }
  607.     if (Filename == NULL) {
  608.     emsg("No filename");
  609.     return FALSE;
  610.     }
  611.     /* clear mem and read file */
  612.     freeall();
  613.     filealloc();
  614.     UNCHANGED;
  615.  
  616.     if (readfile(Filename, Filemem, 0)) {
  617.     emsg("Can't open file");
  618.     return FALSE;
  619.     }
  620.     *Topchar = *Curschar;
  621.     if (line != 1) {
  622.     stuffnumReadbuff(line);
  623.     stuffReadbuff("G");
  624.     }
  625.     setpcmark();
  626.  
  627.     return TRUE;
  628. }
  629.  
  630. static void
  631. doshell()
  632. {
  633.     char           *sh, *getenv();
  634.  
  635.     sh = getenv("SHELL");
  636.     if (sh == NULL) {
  637.     emsg("Shell variable not set");
  638.     return;
  639.     }
  640.     gotocmdline(YES, NUL);
  641.  
  642.     if (system(sh) < 0) {
  643.     emsg("Exec failed");
  644.     return;
  645.     }
  646.     wait_return();
  647. }
  648.  
  649. void
  650. gotocmdline(clr, firstc)
  651.     bool_t          clr;
  652.     char            firstc;
  653. {
  654.     windgoto(Rows - 1, 0);
  655.     if (clr)
  656.     outstr(T_EL);        /* clear the bottom line */
  657.     if (firstc)
  658.     outchar(firstc);
  659. }
  660.  
  661. /*
  662.  * msg(s) - displays the string 's' on the status line 
  663.  */
  664. void
  665. msg(s)
  666.     char           *s;
  667. {
  668.     gotocmdline(YES, NUL);
  669.     outstr(s);
  670. #ifdef AMIGA
  671.     flushbuf();
  672. #endif
  673. #ifdef BSD
  674.     flushbuf();
  675. #endif
  676. }
  677.  
  678. /* VARARGS */
  679. void
  680. smsg(s, a1, a2, a3, a4, a5, a6, a7, a8, a9)
  681.     char           *s;
  682.     int             a1, a2, a3, a4, a5, a6, a7, a8, a9;
  683. {
  684.     char            sbuf[MAX_COLUMNS + 1];
  685.  
  686.     sprintf(sbuf, s, a1, a2, a3, a4, a5, a6, a7, a8, a9);
  687.     msg(sbuf);
  688. }
  689.  
  690. /*
  691.  * emsg() - display an error message 
  692.  *
  693.  * Rings the bell, if appropriate, and calls message() to do the real work 
  694.  */
  695. void
  696. emsg(s)
  697.     char           *s;
  698. {
  699.     UndoInProgress = FALSE;
  700.     RedrawingDisabled = FALSE;
  701.  
  702.     if (P(P_EB))
  703.     beep();
  704.     outstr(T_TI);
  705.     msg(s);
  706.     outstr(T_TP);
  707. #ifdef AMIGA
  708.     flushbuf();
  709. #endif
  710. #ifdef BSD
  711.     flushbuf();
  712. #endif
  713. }
  714.  
  715. void
  716. wait_return()
  717. {
  718.     char            c;
  719.  
  720.     outstr("Press RETURN to continue");
  721.     do {
  722.     c = vgetc();
  723.     } while (c != '\r' && c != '\n');
  724.  
  725.     s_clear();
  726. }
  727.